#include <iostream>
#include <fstream>
#include <vector>

#include "MsgClan.pb.h"
#include "MsgPartnerInfo.pb.h"
#include "MsgMoveTo.pb.h"
#include "pb_cfg_clan.pb.h"
#include "commmsg.h"
#include "../mainctrl.h"
#include "CClanNameHash.h"
#include "CClanMgr.h"
#include "../cmdfront/cmdclan.h"
#include "../cmdinner/CCmdDiceBonus.h"

int CClanMgr::Init(const char * sBuff)
{
    int iBuffOffSet = 0;
    int iRet;
    iRet = _stClanNameHash.Init(sBuff, MAX_CLAN_COUNT_PER_ZONE);
    CHECK_RETURN(iRet);
    iBuffOffSet += _stClanNameHash.CountSize(MAX_CLAN_COUNT_PER_ZONE);

    iRet = _stClanEntryHash.Init(sBuff + iBuffOffSet, MAX_CLAN_COUNT_PER_ZONE);
    CHECK_RETURN(iRet);
    iBuffOffSet += _stClanEntryHash.CountSize(MAX_CLAN_COUNT_PER_ZONE);

    _stClanEntryHash.m_bNeedConstructValue = false;
    for (int i = 0; i < MAX_CLAN_COUNT_PER_ZONE; ++i)
    {
        iRet = _stClanEntryHash[i].Init(sBuff + iBuffOffSet);
        if (iRet)
        {
            CT_ERROR(("%d, %d", i , iRet));
        }

        _stClanEntryHash[i]._stStorage._shMaxBagGridCount = MAX_CLAN_STORAGE_COUNT;
        iBuffOffSet += CClanEntry::CountSize();
    }

    return 0;
}

int CClanMgr::CountSize()
{
    return (_stClanNameHash.CountSize(MAX_CLAN_COUNT_PER_ZONE) +
        _stClanEntryHash.CountSize(MAX_CLAN_COUNT_PER_ZONE) +
        CClanEntry::CountSize() * MAX_CLAN_COUNT_PER_ZONE);
}

int CClanMgr::GetZoneClanNum()
{
    return _stClanEntryHash.Size(CLAN_ENTRY_HASH::USED_LIST);
}

unsigned int CClanMgr::GetNewClanId ()
{
    CZoneData & stZoneData = CMainCtrl::m_pstMain->m_stZoneOnlineData._stZoneData;

    if (GetZoneClanNum() >= MAX_CLAN_COUNT_PER_ZONE)
    {
        return -1;
    }

    int iNowClanId = stZoneData._iNowClanId;
    int iAllocatedId = 1;
    while(1)
    {
        iAllocatedId = stZoneData._uiZoneID * MAX_CLAN_ID_RANGE + iNowClanId;
        int iIndex = _stClanEntryHash.HashFind(iAllocatedId);
        if (iIndex >= 0)
        {
            ++iNowClanId;
            if (iNowClanId > MAX_CLAN_COUNT_PER_ZONE)   //ID取值范围 (1， MAX_CLAN_ID_RANGE)
            {
                iNowClanId = 1;
            }
        }
        else
        {
            break;
        }
    }

    stZoneData._iNowClanId = (iNowClanId + 1);  //保存未分配的起始家族ID
    CVarObjectKey stKey;
    stKey.m_chDataType = DT_ZONE_DATA;
    stKey.m_u64ID = stZoneData._uiZoneID;
    CMainCtrl::m_pstMain->UpdateData(stKey, stZoneData, VOP_UPDATE);

    return iAllocatedId;
}

int CClanMgr::CheckClanName (char * pcName, int iLen)
{
    if (iLen >= CLAN_NAME_STRING_SIZE || iLen <= 0 || NULL == pcName)
    {
        return ERROR_CLAN_NAME_EMPTY;
    }

    CTSizeString<CLAN_NAME_STRING_SIZE> szClanName;
    szClanName.Set("%s", pcName);

    T_SAME_CLAN_NAME_INDEXS aiIndexs;
    _stClanNameHash.HashFind(szClanName, aiIndexs);
    if (aiIndexs.Size() > 0)
    {
        return ERROR_CLAN_NAME_EXIST;
    }

    return 0;
}

int CClanMgr::InitData ()
{
    //初始化家族名称
    for (int i = _stClanEntryHash.GetHeadIndex();
        i >= 0; i = _stClanEntryHash.GetNextIndex(i))
    {
        CClanNameKey stKey;
        CTSizeString<CLAN_NAME_STRING_SIZE> szClanNameWithTail = _stClanEntryHash[i]._szName;
        stKey.Init(szClanNameWithTail);

        int iIndex = _stClanNameHash.HashAlloc(stKey);
        if (iIndex < 0)
        {
            CT_ERROR(("alloc from clanname hash error, ret %d", iIndex));
            return RET_SYS_ERROR;
        }

        CT_TRACE(("get clan name %s, clanid %u", szClanNameWithTail(), _stClanEntryHash[i]._uiClanId));
        _stClanNameHash[iIndex] = _stClanEntryHash[i]._uiClanId;
    }

    _iLastRankTime = 0;
    SortClanRank();
    return 0;
}

int CClanMgr::InitClanData(CClanEntry & stClanEntry, CClanData & stClanData)
{
    stClanEntry.Clear();

    stClanEntry._uiClanId = stClanData._uiClanID;
    stClanEntry._ucAcademyType = stClanData._ucAcademyType;
    stClanEntry._ucClanLevel = stClanData._ucClanLevel;
    stClanEntry._iConstruction = stClanData._iConstruction;
    stClanEntry._iDissMissTime = stClanData._iDismissTime;
    stClanEntry._stMagicDoor = stClanData._stMagicDoor;

    for (int i = 0; i < stClanData._astClanMember.Size(); i++)
    {
        //保证角色身上的家族, 跟家族的实际数据是一致的
        unsigned int uiGid = stClanData._astClanMember[i]._uiGid;
        unsigned int uiSnsClanId = 0;
        int iIndex =  CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(uiGid);
        if (iIndex >= 0)
        {
            uiSnsClanId = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iIndex]._uiClanId;
            if (uiSnsClanId == stClanData._uiClanID)
            {
                stClanEntry.AddNewMember(stClanData._astClanMember[i]);
                continue;
            }
        }

        CT_ERROR(("%d, %d, %d", uiGid, stClanData._uiClanID, uiSnsClanId));
    }

    //如果家族成员为空的话,删除该家族
    if (stClanEntry._astClanMember.Size() <= 0)
    {
        DeleteClanInDB(stClanEntry);
        CT_ERROR(("%d has no memeber and deleted", stClanData._uiClanID));
        return RET_OK;
    }

    //判断族长是否存在. 若无族长, 使用自荐规则,选出族长
    if (false == stClanEntry.HasLeader())
    {
        int iRet = ChooseNewLeader (stClanEntry);
        if (iRet)
        {
            CT_ERROR(("clan has no leader and find error when choose new leader.%d, %d", stClanData._uiClanID, iRet));
            return iRet;
        }
    }

    for (int i = 0; i < stClanData._astJoinReq.Size(); i++)
    {
        unsigned int uiGid = stClanData._astJoinReq[i];
        if (CMainCtrl::m_pstMain->m_stRoleMgr.CheckRoleExist(uiGid))
        {
            stClanEntry._astJoinReq.AddOneItem (uiGid);
        }
    }

    for (int i = 0; i < stClanData._astClanLog.Size(); i++)
    {
        stClanEntry._astClanLog[i] = stClanData._astClanLog[i];
        stClanEntry._astClanLog.m_iUsedCount ++;
    }

    stClanEntry._szNotice =  stClanData._szNotice;
    stClanEntry._uiChangerGid = stClanData._uiChangerGid;
    stClanEntry._szName = stClanData._szName;
    stClanEntry._usClanFightingRank = stClanData._usClanFightingRank;
    stClanEntry._iLastRecommdTime = stClanData._iLastRecommdTime;
    stClanEntry._iAuctionBuffTime = stClanData._iAuctionBuffTime;

    stClanEntry._stClanShop._iLastRefreshTime = stClanData._iLastRefreshTime;
    for (int i = 0; i < stClanData._astItems.Size(); i++)
    {
        CShopItemConfig stItem;
        stItem._ucItemType = stClanData._astItems[i]._ucItemType;
        stItem._usItemConfigID = stClanData._astItems[i]._usItemID;
        stItem._usItemCount = stClanData._astItems[i]._usItemCnt;
        stItem._ucBuyType = ITEM_TYPE_CONTRIBUTION;
        int iRet = CMainCtrl::m_pstMain->m_stItemMgr.GetItemPrice(stItem._ucItemType, stItem._usItemConfigID, stItem._usItemCount,
            ITEM_BUY_PRICE_CONTRI, stItem._iExchangeItemCount);
        if (iRet)
        {
            CT_ERROR(("%d, %d, %d", stItem._ucItemType, stItem._usItemConfigID, iRet));
        }

        stClanEntry._stClanShop._astItems.AddOneItem(stItem);
    }

    int iRet = RET_OK;
    iRet = CMainCtrl::m_pstMain->m_stTreeMgr.InitClanTree(stClanEntry, stClanData);
    if (iRet)
    {
        CT_ERROR (("%d, %d", iRet, stClanData._uiClanID));
        return iRet;
    }

    iRet = CMainCtrl::m_pstMain->m_stStorageMgr.InitData(stClanEntry._stStorage, stClanData._stStorage);
    if (iRet)
    {
        CT_ERROR (("%d, %d", iRet, stClanData._uiClanID));
        return iRet;
    }

    iRet = CMainCtrl::m_pstMain->m_stFamilyDiceMgr.InitClanDice(stClanEntry, stClanData);
    if (iRet)
    {
        CT_ERROR (("%d, %d", iRet, stClanData._uiClanID));
        return iRet;
    }

    stClanEntry._usDailyRecruitCnt = stClanData._usDailyRecruitCnt;

    //如果删号会导致内存中数据和db不一致, 更新入库
    UpdateToDB(stClanData._uiClanID);

    return 0;
}

int CClanMgr::GetClanData(CClanEntry & stClanEntry, CClanData & stClanData)
{
    stClanData.Clear();

    stClanData._uiClanID = stClanEntry._uiClanId;
    stClanData._ucAcademyType = stClanEntry._ucAcademyType;
    stClanData._ucClanLevel = stClanEntry._ucClanLevel;
    stClanData._iConstruction = stClanEntry._iConstruction;
    stClanData._iDismissTime = stClanEntry._iDissMissTime;
    stClanData._stMagicDoor = stClanEntry._stMagicDoor;

    for (int i = 0; i < stClanEntry._astClanMember.Size(); i++)
    {
        stClanData._astClanMember[i]._uiGid = stClanEntry._astClanMember[i]._uiGid;
        stClanData._astClanMember[i]._iLastCalcTime = stClanEntry._astClanMember[i]._iLastCalcTime;
        stClanData._astClanMember[i]._iTodayContri = stClanEntry._astClanMember[i]._iTodayContri;
        stClanData._astClanMember[i]._ucClanTitle = stClanEntry._astClanMember[i]._ucClanTitle;
        stClanData._astClanMember[i]._iAccumContri = stClanEntry._astClanMember[i]._iAccumContri;
        stClanData._astClanMember.m_iUsedCount ++;
    }

    for (int i = 0; i < stClanEntry._astJoinReq.Size();i ++)
    {
        stClanData._astJoinReq[i] = stClanEntry._astJoinReq[i];
        stClanData._astJoinReq.m_iUsedCount ++;
    }

    for (int i = 0; i < stClanEntry._astClanLog.Size(); i ++)
    {
        stClanData._astClanLog[i] = stClanEntry._astClanLog[i];
        stClanData._astClanLog.m_iUsedCount ++;
    }

    stClanData._szNotice =  stClanEntry._szNotice;
    stClanData._uiChangerGid = stClanEntry._uiChangerGid;
    stClanData._szName = stClanEntry._szName;
    stClanData._usClanFightingRank = stClanEntry._usClanFightingRank;
    stClanData._iLastRecommdTime = stClanEntry._iLastRecommdTime;
    stClanData._iAuctionBuffTime = stClanEntry._iAuctionBuffTime;

    stClanData._iLastRefreshTime = stClanEntry._stClanShop._iLastRefreshTime;
    for (int i = 0; i < stClanEntry._stClanShop._astItems.Size(); i++)
    {
        CShopItemData stItem;
        stItem._ucItemType = stClanEntry._stClanShop._astItems[i]._ucItemType;
        stItem._usItemID = stClanEntry._stClanShop._astItems[i]._usItemConfigID;
        stItem._usItemCnt = stClanEntry._stClanShop._astItems[i]._usItemCount;
        stClanData._astItems.AddOneItem(stItem);
    }

    stClanData._stTree = stClanEntry._stTree;
    int iRet = CMainCtrl::m_pstMain->m_stStorageMgr.GetData(stClanEntry._stStorage, stClanData._stStorage);
    CHECK_RETURN(iRet);

    stClanData._stDiceRank = stClanEntry._stDiceRank;
    stClanData._usDailyRecruitCnt = stClanEntry._usDailyRecruitCnt;
    return 0;
}

int CClanMgr::InitUserData(CPlayerDetailInfo& stPlayerDetailInfo, CPlayerSnsDetailInfo & stPlayerSnsDetailInfo)
{
    bool bNeedUpdateDetail = false;
    CUserClan & stUserClan = stPlayerDetailInfo._stUserClan;
    if (stUserClan._uiClanId > 0)
    {
        int iIndex = _stClanEntryHash.HashFind(stUserClan._uiClanId);
        if (iIndex < 0)
        {
            stUserClan.ClearWhenNoClan ();

            stPlayerSnsDetailInfo._stCommInfo._uiClanId = 0;

            bNeedUpdateDetail = true;

            CVarObjectKey stKey;
            stKey.m_chDataType = DT_PLAYER_SNS_DETAIL_INFO;
            stKey.m_u64ID = stPlayerSnsDetailInfo._uiGID;
            CMainCtrl::m_pstMain->UpdateData(stKey, stPlayerSnsDetailInfo, VOP_UPDATE);

            //家族被解散了, 发送离线通知.
            CTips stTips;
            stTips._iType = 22;
            stTips._iData1 = stUserClan._uiClanId;
            CMainCtrl::m_pstMain->SendTips(stPlayerSnsDetailInfo._uiGID, stTips);
        }
    }

    //检查申请加入的家族是否还存在?
    CTArray<unsigned int, MAX_JOIN_CLAN_REQ_PER_USER>  astJoinReqTmp;
    for (int i = 0; i < stUserClan._astJoinReq.Size(); i++)
    {
        int iIndex = _stClanEntryHash.HashFind(stUserClan._astJoinReq[i]);
        if (iIndex >= 0)
        {
            astJoinReqTmp.AddOneItem(stUserClan._astJoinReq[i]);
        }
    }

    if (astJoinReqTmp.Size() != stUserClan._astJoinReq.Size())
    {
        bNeedUpdateDetail = true;
    }

    stUserClan._astJoinReq = astJoinReqTmp;
    if (bNeedUpdateDetail)
    {
        CVarObjectKey stKey;
        stKey.m_chDataType = DT_PLAYER_DETAIL_INFO;
        stKey.m_u64ID = stPlayerDetailInfo._uiGID;
        CMainCtrl::m_pstMain->UpdateData(stKey, stPlayerDetailInfo, VOP_UPDATE);
    }

    return 0;
}

int CClanMgr::GetUserData(CUserClan & stUserClanData)
{
    return 0;
}

int CClanMgr::LoadConfig(const char* szCfgPath)
{
    int iRet = 0;

    if (szCfgPath == NULL)
    {
        CT_ERROR(("clan Cfg Path is null"));
        return RET_SYSTEM_FAIL;
    }

    pb_cfg_clan stClanPBConfig;
    std::fstream stFileInput(szCfgPath, std::ios::in | std::ios::binary);
    if (!stClanPBConfig.ParseFromIstream(&stFileInput))
    {
        CT_ERROR(("Failed to Parse stClanPBConfig! %s", szCfgPath));
        return RET_SYSTEM_FAIL;
    }

    for (int i = 0; i < stClanPBConfig.clan_build_rows_size(); i++)
    {
        const pb_cfg_clan_pb_cfg_clan_build_unit & stClanPbConfigUnit
            = stClanPBConfig.clan_build_rows(i);

        CClanBuildConfig stConfigData;
        stConfigData._bconfigured = true;
        stConfigData._iConstructionCost = stClanPbConfigUnit.construction_cost();
        stConfigData._shExpAddtion = (short)(stClanPbConfigUnit.exp_addition() & 0x7FFF);
        stConfigData._shSilverAddtion = (short)(stClanPbConfigUnit.silver_addition() & 0X7FFF);
        stConfigData._ucShopItemCnt = (unsigned char)(stClanPbConfigUnit.shop_item_cnt());
        iRet = _stClanConfig.AddClanBuildConfig(stClanPbConfigUnit.clan_level(), stConfigData);
        if (iRet)
        {
            CT_ERROR (("%d", iRet, stClanPbConfigUnit.clan_level()));
            return iRet;
        }
    }

    for (int i = 0; i < stClanPBConfig.clan_skill_rows_size(); i++)
    {
        const pb_cfg_clan_pb_cfg_clan_skill_unit & stClanPbConfigUnit
            = stClanPBConfig.clan_skill_rows(i);

        CClanSkillConfig stConfigData;
        stConfigData._bconfigured = true;
        stConfigData._ucSkillId = stClanPbConfigUnit.skill_id();
        stConfigData._ucAttrType = stClanPbConfigUnit.attr();
        stConfigData._shPerAttrValue = stClanPbConfigUnit.attr_addition();
        stConfigData._shOpenLevel = stClanPbConfigUnit.open_level();
        iRet = _stClanConfig.AddSkillConfig(stConfigData._ucSkillId, stConfigData);
        if (iRet)
        {
            CT_ERROR (("%d", stConfigData._ucSkillId));
            return iRet;
        }
    }

    for (int i = 0; i < stClanPBConfig.clan_learn_skill_rows_size(); i++)
    {
        const pb_cfg_clan_pb_cfg_clan_learn_skill_unit & stClanPbConfigUnit
            = stClanPBConfig.clan_learn_skill_rows(i);

        CSkillContriConfig stConfigData;
        if (strlen(stClanPbConfigUnit.skill_list().c_str()) > 0)
        {
            const std::string sDel = ",";
            std::string sSkillList = stClanPbConfigUnit.skill_list();
            std::vector<std::string> vSkillList;

            std::string sContriCost = stClanPbConfigUnit.contribution_cost();
            std::vector<std::string> vContriCost;

            CTStringUtils::Split(sSkillList, sDel, vSkillList);
            CTStringUtils::Split(sContriCost, sDel, vContriCost);
            if (vSkillList.size() != MAX_CLAN_SKILL_NUM
                || vContriCost.size() != MAX_CLAN_SKILL_NUM )
            {
                CT_ERROR(("%d, %d, %d", vSkillList.size(), vContriCost.size()));
                return ERROR_CLAN_SKILL_CONFIG;
            }

            for (unsigned int i = 0; i < vSkillList.size(); ++i)
            {
                unsigned char ucSkillId = (unsigned char)atoi(vSkillList[i].c_str());
                int iContriCost = atoi(vContriCost[i].c_str());
                iRet = stConfigData.AddContriCost (ucSkillId, iContriCost);
                if (iRet)
                {
                    CT_ERROR(("%d, %d", ucSkillId, iContriCost));
                    return ERROR_CLAN_SKILL_CONFIG;
                }
            }
        }

        iRet = _stClanConfig.AddSkillContriConfig(stClanPbConfigUnit.skill_level(), stConfigData);
        if (iRet)
        {
            CT_ERROR (("%d", stClanPbConfigUnit.skill_level()));
            return iRet;
        }
    }
    return 0;
}

int CClanMgr::GetClanConfig (unsigned int uiClanId, CClanBuildConfig & stClanBuild)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    int iRet = _stClanConfig.GetClanBuildConfig(_stClanEntryHash[iIndex]._ucClanLevel, stClanBuild);
    if (iRet)
    {
        CT_ERROR(("id = %d, level = %d", uiClanId, _stClanEntryHash[iIndex]._ucClanLevel));
        return iRet;
    }

    return 0;
}

int CClanMgr::CreateClan(CPlayerBaseInfo & stPlayerBaseInfo, CUserClan & stUserClan, char * pcName, int iLen)
{
    int uiGid = stPlayerBaseInfo._uiGID;
    if (stPlayerBaseInfo._iGold < CREATE_CLAN_NEED_GOLD)
    {
        return  ERROR_GOLD_LESS;
    }

    //创建者是否在其它家族中
    if (0 != stUserClan._uiClanId)
    {
        return ERROR_ALREADY_IN_CLAN;
    }

    //检查学院是否非法
    if (stPlayerBaseInfo._ucAcademy <= ACADEMY_TYPE_NONE || stPlayerBaseInfo._ucAcademy > ACADEMY_TYPE_HUFF)
    {
        return ERROR_CLAN_ACADEMY_INVALID;
    }

    int iRet = RET_OK;

    //检查是否有同名家族
    iRet = CheckClanName(pcName, iLen);
    CHECK_RETURN(iRet);

    //使用跟主角昵称相同的长度限制
    int iNameLen = CTStringUtils::GetUtf8StrLen(pcName);
    if (iNameLen < MIN_ROLENAME_LEN || iNameLen > MAX_ROLENAME_LEN)
    {
        return ERROR_WRONG_NAME;
    }

    unsigned uiClanId = GetNewClanId();
    if (uiClanId <= 0)
    {
        return ERROR_ZONE_CLAN_NUM_FULL;
    }

    //保存家族名字
    CTSizeString<CLAN_NAME_STRING_SIZE> szNameWithTail;
    szNameWithTail.Set("%s@%d", pcName, CMainCtrl::m_pstMain->ZoneID());

    CClanNameKey stClanNameKey;
    stClanNameKey.Init(szNameWithTail);
    int iNameIndex = _stClanNameHash.HashAlloc(stClanNameKey);
    if (iNameIndex < 0)
    {
        CT_ERROR(("add to clan name hash error, ret %d", iNameIndex));
        return ERROR_ZONE_CLAN_NUM_FULL;
    }

    int iEntryIndex = _stClanEntryHash.HashAlloc(uiClanId);
    if (iEntryIndex < 0)
    {
        CT_ERROR(("%d", uiClanId));
        return ERROR_ZONE_CLAN_NUM_FULL;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iEntryIndex];

    DelAllJoinRequest(uiGid, stUserClan);
    _stClanNameHash[iNameIndex] = uiClanId;

    //扣除金币
    CMainCtrl::m_pstMain->m_stItemMgr.SubGold(stPlayerBaseInfo, OSS_MONEY_MODULE_17, CREATE_CLAN_NEED_GOLD);

    //初始化家族
    stClanEntry.Clear();
    stClanEntry._ucClanLevel = 1;
    stClanEntry._szName = szNameWithTail;
    stClanEntry._uiClanId = uiClanId;
    stClanEntry._ucAcademyType = stPlayerBaseInfo._ucAcademy;
    stClanEntry._usClanRank = GetZoneClanNum();   //新创建的家族默认排到最后
    stClanEntry._stMagicDoor.Clear();   //初始化家族魔界门

    iRet = AddToClan (uiGid, stUserClan, stClanEntry, CLAN_MEMBER_LEADER);
    if (iRet)
    {
        CT_ERROR (("%d, %d", iRet, uiGid));
        return iRet;
    }

    CClanLog stLog;
    stLog._uiGid = uiGid;
    stLog._uiEvent = CLAN_LOG_CREATE;
    AddClanLog(stClanEntry, stLog);

    SortClanRank();
    return 0;
}

int CClanMgr::DeleteClan (unsigned int uiGid, CUserClan & stUserClan, unsigned int uiClanId)
{
    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiGid, stClanEntry, (unsigned char)CLAN_REQ_DELETE_CLAN);
    CHECK_RETURN(iRet);

    if ((stClanEntry._ucClanLevel >= MAX_CLAN_DELAY_DELETE_LEVEL)
        && (0 == stClanEntry._iDissMissTime))
    {
        stClanEntry._iDissMissTime = CTTimeUtils::GetDayBeginSec(CMainCtrl::m_pstMain->m_iNow) + (MAX_CLAN_DELAY_DELETE_TIME)
            + (MAX_CLAN_DELAY_DELETE_DAY - 1)*86400;    //延迟到(第三天的23:00)解散
        return 0;
    }

    iRet = DelFromClan(uiGid, stUserClan, stClanEntry);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d", uiGid, uiClanId, iRet));
        return iRet;
    }

    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiGid, NOTIFY_ROLE_INFO | NOTIFY_MAP_INFO);

    CCmdDeleteClan * pstCmd = (CCmdDeleteClan *)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_DELETE_CLAN);
    if (!pstCmd)
    {
        CT_WARNING(("create CCmdDeleteClan error. %d, %d", uiGid, uiClanId));
        return RET_SYSTEM_FAIL;
    }

    pstCmd->_auiClanId.AddOneItem(uiClanId);
    pstCmd->pstMainCtrl = CMainCtrl::m_pstMain;
    pstCmd->Do(NULL);
    return 0;
}

int CClanMgr::DeleteClanInDB (CClanEntry & stClanEntry)
{
    CClanData stClanData;
    CVarObjectKey stKey;
    stKey.m_chDataType = DT_CLAN_DATA;
    stKey.m_u64ID = stClanEntry._uiClanId;
    stClanData._uiClanID = stClanEntry._uiClanId;
    CMainCtrl::m_pstMain->UpdateData(stKey, stClanData, VOP_DELETE);

    //删除家族名称
    CClanNameKey stNameKey;
    stNameKey.Init(stClanEntry._szName);
    _stClanNameHash.HashFreeByKey(stNameKey);
    _stClanEntryHash.HashFreeByKey(stClanEntry._uiClanId);

    //设置了_stClanEntryHash.m_bNeedConstructValue = false, Free以后 stClanEntry的构造函数不会被调用, 需要主动调用一次清理函数
    stClanEntry.Clear();
    stClanEntry._stStorage.Clear();

    //为了保证排名的连续性,重新排序一次
    SortClanRank ();

    return 0;
}

int CClanMgr::CancelDelete (unsigned int uiGid, CUserClan & stUserClan, unsigned int uiClanId)
{
    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiGid, stClanEntry, (unsigned char)CLAN_CANCEL_DELETE);
    CHECK_RETURN(iRet);

    if (0 == stClanEntry._iDissMissTime)
    {
        return ERROR_CANCEL_CLAN_DELETE;
    }

    stClanEntry._iDissMissTime = 0;
    return 0;
}

int CClanMgr::TransferClan (unsigned int uiGid,  unsigned int uiClanId, unsigned int uiOperGId)
{
    if (uiGid == uiOperGId)
    {
        return ERROR_TRANS_CLAN_TITLE;
    }

    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiGid, stClanEntry, CLAN_TRANS_LEADER_TITLE);
    CHECK_RETURN(iRet);

    CClanMember stMember;
    iRet = stClanEntry.GetClanMember(uiOperGId, stMember);
    CHECK_RETURN(iRet);

    if (CLAN_MEMBER_VICE != stMember._ucClanTitle)
    {
        return ERROR_TRANS_CLAN_TITLE;
    }

    iRet = stClanEntry.SetMemberTitle (uiGid, CLAN_MEMBER_NORM);
    if (iRet)
    {
        CT_ERROR((""));
    }

    stClanEntry.SetMemberTitle (uiOperGId, CLAN_MEMBER_LEADER);
    if (iRet)
    {
        CT_ERROR((""));
    }
    return 0;
}

int CClanMgr::CheckPermission (unsigned int uiGid, CClanEntry & stClanEntry, unsigned char ucOperType)
{
    CClanMember stMember;
    int iRet = stClanEntry.GetClanMember(uiGid, stMember);
    CHECK_RETURN(iRet);

    switch (ucOperType)
    {
    case CLAN_JOIN_REQ_ACCEPT:
    case CLAN_JOIN_REQ_REJECT:
    case CLAN_CHANGE_NOTICE:
    case CLAN_FORCE_QUIT_CLAN:
    case CLAN_UPGRADE_CLAN:
    case CLAN_CANCEL_DELETE:
    case CLAN_SEND_RECRUIT:
        {
            if ((CLAN_MEMBER_LEADER != stMember._ucClanTitle)
                && (CLAN_MEMBER_VICE != stMember._ucClanTitle))
            {
                return ERROR_CLAN_NO_PERMISSION;
            }
        }
        break;

    case CLAN_APPOINT_TITLE:
    case CLAN_REQ_DELETE_CLAN:
    case CLAN_TRANS_LEADER_TITLE:
    case CLAN_ALLOT_BONUS:
        {
            if (CLAN_MEMBER_LEADER != stMember._ucClanTitle)
            {
                return ERROR_CLAN_NO_PERMISSION;
            }
        }
        break;

    case CLAN_REQ_QUIT_CLAN:
        {
            if (CLAN_MEMBER_LEADER == stMember._ucClanTitle)
            {
                return ERROR_LEADER_CANT_QUIT;
            }
        }
        break;

    default:
        return ERROR_CLAN_UNKONW_OPER;
    }

    return 0;
}

int CClanMgr::HandleJoinRequest (unsigned int uiHandlerGid, unsigned int uiClanId,
                                 CUserClan & stReqUserClan, unsigned int uiReqGid, unsigned char ucOperType)
{
    if ((CLAN_JOIN_REQ_ACCEPT != ucOperType)
        && (CLAN_JOIN_REQ_REJECT != ucOperType))
    {
        return ERROR_CLAN_PARA_INVALID;
    }

    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiHandlerGid, stClanEntry, ucOperType);
    CHECK_RETURN(iRet);

    //删除家族中的申请,请求不存在的话,会返回错误
    iRet = CancelJoinRequest (uiReqGid, stReqUserClan, uiClanId);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d, %d", uiReqGid, uiClanId, stReqUserClan._uiClanId,iRet));
        return iRet;
    }

    //判断成员是否已满
    if (stClanEntry.IsMemberFull())
    {
        return ERROR_CLAN_MEMBER_FULL;
    }

    //取消用户申请的其它家族
    DelAllJoinRequest(uiReqGid, stReqUserClan);

    //加入到家族中
    if (CLAN_JOIN_REQ_ACCEPT == ucOperType)
    {
        iRet = AddToClan (uiReqGid, stReqUserClan, stClanEntry, CLAN_MEMBER_NORM);
        if (iRet)
        {
            CT_ERROR(("%d, %d, %d, %d, %d", iRet, uiHandlerGid, uiReqGid, uiClanId, stReqUserClan._uiClanId));
            return iRet;
        }

        CClanLog stLog;
        stLog._uiGid = uiReqGid;
        stLog._uiEvent = CLAN_LOG_JOIN;
        AddClanLog(stClanEntry, stLog);
    }

    return 0;
}

int CClanMgr::CancelJoinRequest (unsigned int uiReqGid, CUserClan & stReqUserClan, unsigned int uiClanId)
{
    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = stClanEntry.DelJoinReq(uiReqGid);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d", uiReqGid, uiClanId, iRet));
        return iRet;
    }

    UpdateToDB(uiClanId);

    iRet = stReqUserClan.DelJoinReq(uiClanId);
    CHECK_RETURN(iRet);

    return 0;
}

int CClanMgr::ChangeNotice (unsigned int uiReqGid, unsigned int uiClanId, char * pcNotice, int iLen)
{
    if (0 == pcNotice)
    {
        return ERROR_CLAN_NOTICE_LEN;
    }

    if (0 != iLen)   //清除家族公告时,前台会发送0长度的字符串过来
    {
        int iStrLen = CTStringUtils::GetUtf8StrLen(pcNotice);
        if (iStrLen <= 0 || iStrLen > (MAX_CLAN_NOTICE_STRING_LEN / 3 * 2))
        {
            CT_ERROR(("%s, %d", pcNotice, iStrLen));
            return ERROR_CLAN_NOTICE_LEN;
        }
    }

    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiReqGid, stClanEntry, CLAN_CHANGE_NOTICE);
    CHECK_RETURN (iRet);

    stClanEntry._szNotice.Set("%s", pcNotice, iLen);
    stClanEntry._uiChangerGid = uiReqGid;
    return 0;
}

int CClanMgr::AppointTitle (unsigned int uiHandlerGid, CUserClan & stHandlerClan, unsigned int uiOperGid, unsigned char ucNewTitle)
{
    if (uiHandlerGid == uiOperGid)
    {
        return ERROR_CLAN_OPER_MYSELF;
    }

    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(stHandlerClan._uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiHandlerGid, stClanEntry, CLAN_APPOINT_TITLE);
    CHECK_RETURN (iRet);

    if (CLAN_MEMBER_VICE != ucNewTitle
        && CLAN_MEMBER_NORM!= ucNewTitle)
    {
        return ERROR_CLAN_MEMBER_TITLE;
    }

    iRet = stClanEntry.SetMemberTitle(uiOperGid, ucNewTitle);
    CHECK_RETURN (iRet);

    CClanLog stLog;
    stLog._uiGid = uiOperGid;
    if (CLAN_MEMBER_VICE == ucNewTitle)
    {
        stLog._uiEvent = CLAN_LOG_VICE;
    }
    else
    {
        stLog._uiEvent = CLAN_LOG_NORM;
    }

    AddClanLog(stClanEntry, stLog);

    return 0;
}

int CClanMgr::QuitClan (unsigned int uiReqGid, CUserClan & stReqUserClan)
{
    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(stReqUserClan._uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    //家族里没有该成员,返回成功，方便外面的逻辑处理
    if (!stClanEntry.HasThisMember(uiReqGid))
    {
        CT_ERROR(("%d, %d", uiReqGid, stReqUserClan._uiClanId));

        stReqUserClan.ClearWhenNoClan ();
        return 0;
    }

    //检查权限, 族长无法退出
    int iRet = CheckPermission(uiReqGid, stClanEntry, CLAN_REQ_QUIT_CLAN);
    CHECK_RETURN(iRet);

    iRet = DelFromClan(uiReqGid, stReqUserClan, stClanEntry);
    if (iRet)
    {
        CT_ERROR(("%d, %d", iRet, uiReqGid, stReqUserClan._uiClanId));
        return iRet;
    }

    CClanLog stLog;
    stLog._uiGid = uiReqGid;
    stLog._uiEvent = CLAN_LOG_QUIT;
    AddClanLog(stClanEntry, stLog);

    return 0;
}

int CClanMgr::ForceQuitClan (unsigned int uiLeaderGid, CUserClan & stOuterClan, unsigned int uiOuterGID)
{
    int iIndex = _stClanEntryHash.HashFind(stOuterClan._uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission (uiLeaderGid, stClanEntry, CLAN_FORCE_QUIT_CLAN);
    CHECK_RETURN(iRet);

    iRet = DelFromClan(uiOuterGID, stOuterClan, stClanEntry);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d", iRet, uiOuterGID, stOuterClan._uiClanId));
        return iRet;
    }

    CClanLog stLog;
    stLog._uiGid = uiOuterGID;
    stLog._uiEvent = CLAN_LOG_FORCE;
    AddClanLog(stClanEntry, stLog);

    return 0;
}

int CClanMgr::RequestJoinClan (CPlayerBaseInfo & stPlayerBaseInfo, unsigned int uiClanId, CUserClan & stUserClan)
{
    unsigned int uiGid = stPlayerBaseInfo._uiGID;
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(uiGid);
    if (iIndex < 0)
    {
        return ERROR_NO_SUCH_USER;
    }

    if (stUserClan._uiClanId > 0)
    {
        return ERROR_ALREADY_IN_CLAN;
    }

    if (stPlayerBaseInfo._ucAcademy <= ACADEMY_TYPE_NONE || stPlayerBaseInfo._ucAcademy > ACADEMY_TYPE_HUFF)
    {
        return ERROR_CLAN_ACADEMY_INVALID;
    }

    //检查家族是否存在
    iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    if (stPlayerBaseInfo._ucAcademy != stClanEntry._ucAcademyType)
    {
        return ERROR_CLAN_ACADEMY_WRONG;
    }

    int iRet = stClanEntry.AddJoinReq(uiGid);
    CHECK_RETURN(iRet);

    //先检查之前申请过的家族是否还在
    CTArray<unsigned int, MAX_JOIN_CLAN_REQ_PER_USER>  astToDelReq;
    for (int i = 0; i < stUserClan._astJoinReq.Size(); ++i)
    {
        iIndex = _stClanEntryHash.HashFind(stUserClan._astJoinReq[i]);
        if (iIndex < 0)
        {
            astToDelReq.AddOneItem(stUserClan._astJoinReq[i]);
        }
    }

    //删除掉不存在的请求, 再加入新的请求
    for (int i = 0; i < astToDelReq.Size(); i++)
    {
        stUserClan.DelJoinReq(astToDelReq[i]);
    }

    iRet = stUserClan.AddJoinReq(uiClanId);
    CHECK_RETURN(iRet);

    return 0;
}

int CClanMgr::GetClanEntry (CUserClan & stUserClan, CClanEntry & stClanEntry)
{
    return GetClanEntry(stUserClan._uiClanId, stClanEntry);
}

int CClanMgr::GetClanEntry (unsigned int uiClanId, CClanEntry & stClanEntry)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    stClanEntry = _stClanEntryHash[iIndex];
    return 0;
}

int CClanMgr::GetClanIndexByClanId (unsigned int uiClanId)
{
    return _stClanEntryHash.HashFind(uiClanId);
}

//不能传入uiClanId, 在遍历的时候会出问题. 调用hash的Find接口会影响CTHashObjectMgr::GetNextIndex的结果
int CClanMgr::GetClanBaseInfo (CClanEntry & stClanEntry, CUserClan & stUserClan, MsgClanBaseInfo & stAns)
{
    stAns.set_clan_id(stClanEntry._uiClanId);

    unsigned int uiLeaderGid;
    int iRet = stClanEntry.GetClanLeader (uiLeaderGid);
    CHECK_RETURN(iRet);

    int iGidIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(uiLeaderGid);
    if (iGidIndex < 0)
    {
        return ERROR_NO_SUCH_USER;
    }

    stAns.set_clan_rank(stClanEntry._usClanRank);
    stAns.set_clan_name(stClanEntry._szName());
    stAns.set_clan_fighting_rank(CMainCtrl::m_pstMain->m_stClanBattleMgr.GetClanRank(stClanEntry._uiClanId));
    stAns.set_leader_id(uiLeaderGid);
    stAns.set_leader_name(CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iGidIndex]._sRoleName());
    stAns.set_clan_level(stClanEntry._ucClanLevel);
    stAns.set_clan_member_count(stClanEntry._astClanMember.Size());
    stAns.set_max_member_count(stClanEntry.GetMaxMemberCount());
    stAns.set_academy_type(stClanEntry._ucAcademyType);
    stAns.set_construction(stClanEntry._iConstruction);

    //设置是否申请过帮派的标志
    if (0 == stUserClan._uiClanId)
    {
        stAns.set_have_requested(false);
        for (int i = 0; i < stUserClan._astJoinReq.Size(); i++)
        {
            if (stClanEntry._uiClanId == stUserClan._astJoinReq[i])
            {
                stAns.set_have_requested(true);
                break;
            }
        }
    }
    else
    {
        stAns.set_have_requested(false);
    }

    int iUsedCnt = stClanEntry.GetTodayRecuitCnt(CMainCtrl::m_pstMain->m_iNow);
    stAns.set_free_recruit_cnt(CT_MAX(0, (MAX_RECURIT_FREE_CNT - iUsedCnt)));

    return 0;
}

int CClanMgr::GetClanMemberInfo(unsigned int uiClanId, unsigned int uiMemberGid, MsgClanMemberInfo & stAns)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    CClanMember stMember;
    int iRet = _stClanEntryHash[iIndex].GetClanMember(uiMemberGid, stMember);
    CHECK_RETURN (iRet);

    stAns.set_gid(stMember._uiGid);
    int iGidIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(stMember._uiGid);
    if (iGidIndex < 0)
    {
        CT_ERROR (("Get clan (id = %u) info, member: %u not exist.", uiClanId, stMember._uiGid));
        return ERROR_CLAN_LACK_SNS_INFO;
    }
    CPlayerSnsCommInfo & stCommInfo = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iGidIndex];

    CArenaRank stArenaRank;
    iRet = CMainCtrl::m_pstMain->m_stArenaMgr.GetRankInfo(stMember._uiGid, stArenaRank);
    if (iRet)
    {
        stArenaRank._iNowRank = 0;   //到竞技场里挑战后才能获取排名!  未挑战过,默认排名为0.
    }

    stAns.set_arena_rank(stArenaRank._iNowRank);
    stAns.set_is_male(stCommInfo._ucIsMale);
    stAns.set_name(stCommInfo._sRoleName());
    stAns.set_clan_title(stMember._ucClanTitle);
    stAns.set_role_level(stCommInfo._usLevel);
    if (CTTimeUtils::IsSameDay(stMember._iLastCalcTime, CMainCtrl::m_pstMain->m_iNow))
    {
        stAns.set_contribution(stMember._iTodayContri);  //今日贡献度
    }
    else
    {
        stAns.set_contribution(0);   //今日贡献度
    }

    stAns.set_total_contribution(stMember._iAccumContri);
    if (CMainCtrl::m_pstMain->m_stPlayerOnline.CheckOnline(uiMemberGid))
    {
        stAns.set_last_login_time(0);
    }
    else
    {
        stAns.set_last_login_time(CMainCtrl::m_pstMain->m_iNow - stCommInfo._iLastLogOutTime);  //累计离线时间
    }

    return 0;
}

int CClanMgr::GetClanDetailInfo (unsigned int uiClanId, MsgClanDetailInfo & stAns)
{
    //请求某个家族的详细信息
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    stAns.set_clan_id(uiClanId);
    stAns.set_notice(_stClanEntryHash[iIndex]._szNotice());

    unsigned int uiChanger = _stClanEntryHash[iIndex]._uiChangerGid;
    if (0 != uiChanger)
    {
        int iGidIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(uiChanger);
        if (iGidIndex >= 0)
        {
            stAns.set_notice_changer(CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iGidIndex]._sRoleName());
        }
        else
        {
            CT_ERROR (("Get clan (id = %u) info, member: %u not exist.", uiClanId, uiChanger));
        }
    }

    bool bSelfCommend = false;
    for (int i = 0; i < _stClanEntryHash[iIndex]._astClanMember.Size(); i++)
    {
        CClanMember & stMember = _stClanEntryHash[iIndex]._astClanMember[i];
        MsgClanMemberInfo * pstMember = stAns.add_member_list();
        GetClanMemberInfo(uiClanId, stMember._uiGid, *pstMember);
        if ((CLAN_MEMBER_LEADER == _stClanEntryHash[iIndex]._astClanMember[i]._ucClanTitle)
            && (pstMember->last_login_time() >= MAX_CLAN_RECOMMD_TIME))
        {
            bSelfCommend = true;
        }
    }

    stAns.set_self_commend(bSelfCommend);
    stAns.set_expired_time(_stClanEntryHash[iIndex]._iDissMissTime);
    return 0;
}

int CClanMgr::GetClanName (unsigned uiClanId, CTSizeString<CLAN_NAME_STRING_SIZE> & stClanName)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        stClanName.Set("");
        return ERROR_CLAN_NOT_EXIST;
    }

    stClanName.Set(_stClanEntryHash[iIndex]._szName());
    return 0;
}

//ucLogType=2为成员动态, 7为家族事件
int CClanMgr::GetClanLog (unsigned int uiClanId, unsigned char ucLogType, MsgAnsUserClanInfo & stAns)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    return GetClanLog(stClanEntry, ucLogType, stAns);
}

int CClanMgr::GetClanSkill (CUserClan & stUserClan, MsgAnsUserClanInfo & stAns)
{
    stAns.set_oper_type(CLAN_INFO_SKILL);

    MsgClanSkillInfo * pstSkillInfo = stAns.mutable_clan_skills();
    pstSkillInfo->set_clan_level(GetClanLevel(stUserClan._uiClanId));
    for (int i = 0; i < stUserClan._astClanSkill.Size(); i++)
    {
        MsgTypeValuePair * pstSkill = pstSkillInfo->add_clan_skills();
        pstSkill->set_type(stUserClan._astClanSkill[i]._ucType);
        pstSkill->set_value(stUserClan._astClanSkill[i]._iValue);
    }

    return 0;
}

int CClanMgr::GetClanSkillInfo(CUserClan & stUserClan, MsgClanSkillInfo & stSkillInfo)
{

    stSkillInfo.set_clan_level(GetClanLevel(stUserClan._uiClanId));
    for (int i = 0; i < stUserClan._astClanSkill.Size(); i++)
    {
        MsgTypeValuePair * pstSkill = stSkillInfo.add_clan_skills();
        pstSkill->set_type(stUserClan._astClanSkill[i]._ucType);
        pstSkill->set_value(stUserClan._astClanSkill[i]._iValue);
    }

    return 0;
}


int CClanMgr::GetSkillAttr(CUserClan & stUserClan, CPlayerAttr & stPlayerAttr)
{
    for (int i = 0; i < stUserClan._astClanSkill.Size(); i++)
    {
        CAttrPair & stSkill = stUserClan._astClanSkill[i];
        if (stSkill._iValue > 0)
        {
            CClanSkillConfig stConfigData;
            int iRet = _stClanConfig.GetSkillConfig(stSkill._ucType, stConfigData);
            if (iRet)
            {
                CT_ERROR(("%d, %d", stSkill._ucType, iRet));
                continue;
            }

            CT_TRACE(("%d,%d,%d,%d", stSkill._ucType, stSkill._iValue, stConfigData._ucAttrType, stSkill._iValue * stConfigData._shPerAttrValue));
            stPlayerAttr.AddAttr(stConfigData._ucAttrType, stSkill._iValue * stConfigData._shPerAttrValue);
        }
    }

    return 0;
}

int CClanMgr::GetSkillStatusAttr(CUserClan& stUserClan,
                                 CUserClan::T_CLAN_SKILL& astClanSkill)
{
    int iRet = 0;
    astClanSkill.Clear();
    CAttrPair stAttrPair;
    CPlayerAttr stPlayerAttr;
    stPlayerAttr.Clear();

    CTLib::CTArray<int, EXT_ATTR_MAX_COUNT> aiSkillID;
    aiSkillID.m_iUsedCount = aiSkillID.GetCount();
    for (int i = 0; i < aiSkillID.Size(); ++i)
    {
        aiSkillID[i] = 0;
    }

    CClanSkillConfig stConfigData;
    for (int i = 0; i < stUserClan._astClanSkill.Size(); i++)
    {
        CAttrPair& stSkill = stUserClan._astClanSkill[i];
        iRet = _stClanConfig.GetSkillConfig(stSkill._ucType, stConfigData);
        if (iRet)
        {
            CT_ERROR(("%d, %d", stSkill._ucType, iRet));
            continue;
        }
        if (stConfigData._ucAttrType >= EXT_ATTR_MAX_COUNT)
        {
            CT_ERROR(("skill(%d) type(%d)",stSkill._ucType,
                stConfigData._ucAttrType));
            continue;
        }
        if (aiSkillID[stConfigData._ucAttrType] <= 0)
        {
            aiSkillID[stConfigData._ucAttrType] = stSkill._ucType;
        }
        if (stSkill._iValue > 0)
        {
            stPlayerAttr.AddAttr(stConfigData._ucAttrType,
                stSkill._iValue * stConfigData._shPerAttrValue);
        }
    }

    for (int i = 0; i < aiSkillID.Size(); ++i)
    {
        if (aiSkillID[i] > 0 && stPlayerAttr.GetAttr(i) > 0)
        {
            stAttrPair._ucType = aiSkillID[i];//给到前端的不是属性类型而是ID
            stAttrPair._iValue = stPlayerAttr.GetAttr(i);
            astClanSkill.AddOneItem(stAttrPair);
        }
    }

    return 0;
}

int CClanMgr::CheckAndSortClan ()
{
    if(CMainCtrl::m_pstMain->m_iNow - _iLastRankTime < MAX_CLAN_SORT_TIME)
    {
        return 0;
    }

    CT_TRACE (("check and sort clan rank. time %u", CMainCtrl::m_pstMain->m_iNow));

    return SortClanRank();
}

int CClanMgr::SortClanRank()
{
    //排序规则: 家族等级 >　家族贡献度 > 家族成员数量
    //触发排序时间: 1.每半个小时, 2.家族解散

    _astClanRank.Clear();

    for (int iIndex = _stClanEntryHash.GetHeadIndex();
        iIndex >= 0;
        iIndex = _stClanEntryHash.GetNextIndex(iIndex))
    {
        CClanEntry & stCurEntry =  _stClanEntryHash[iIndex];

        unsigned int uiLeaderGid = 0;
        if (stCurEntry.GetClanLeader(uiLeaderGid) != RET_OK)
        {
            continue;
        }

        int j = 0;
        for (; j < _astClanRank.Size(); j++)
        {
            CClanEntry & stTmp = _stClanEntryHash[_astClanRank[j]];
            bool bIsRankTop_1 = (stCurEntry._ucClanLevel > stTmp._ucClanLevel) ? true : false;
            bool bIsRankTop_2 = (stCurEntry._ucClanLevel == stTmp._ucClanLevel
                && stCurEntry._iConstruction > stTmp._iConstruction) ? true : false;
            bool bIsRankTop_3 = (stCurEntry._ucClanLevel == stTmp._ucClanLevel
                && stCurEntry._iConstruction == stTmp._iConstruction
                && stCurEntry._astClanMember.Size() >= stTmp._astClanMember.Size()) ? true : false;

            if (bIsRankTop_1 || bIsRankTop_2 || bIsRankTop_3)
            {
                break;
            }
        }

        for (int k = _astClanRank.m_iUsedCount; k > j; --k)
        {
            _astClanRank[k] = _astClanRank[k - 1];
        }

        _astClanRank.m_iUsedCount += 1;
        _astClanRank[j] = iIndex;
    }

    for (int i = 0; i < _astClanRank.Size(); i++)
    {
        _stClanEntryHash[_astClanRank[i]]._usClanRank = (i + 1);
        CT_TRACE (("Clan level: %u, construction: %d, clan rank: %u", _stClanEntryHash[_astClanRank[i]]._ucClanLevel,
            _stClanEntryHash[_astClanRank[i]]._iConstruction, (i + 1)));
    }

    _iLastRankTime = CMainCtrl::m_pstMain->m_iNow;
    return 0;
}

//检查并处理家族相关事件
void CClanMgr::TimeTick (int iNowTime)
{
    static int iNextProcRecomdTime = CTTimeUtils::GetDayBeginSec(iNowTime) + (CTTimeUtils::GetHourFromTs(iNowTime) + 1) * 3600;
    static int iNextProcDelTime = CTTimeUtils::GetDayBeginSec(iNowTime) + MAX_CLAN_DELAY_DELETE_TIME;
    static int iNextProcDiceTime = CMainCtrl::m_pstMain->m_stFamilyDiceMgr.GetAccountTime (iNowTime);

    //1. 每个小时处理一次自荐
    if (iNowTime >= iNextProcRecomdTime)
    {
        iNextProcRecomdTime = CTTimeUtils::GetDayBeginSec(iNowTime) + (CTTimeUtils::GetHourFromTs(iNowTime) + 1) * 3600;

        for (int iIndex = _stClanEntryHash.GetHeadIndex();
            iIndex >=0 ;
            iIndex = _stClanEntryHash.GetNextIndex(iIndex))
        {
            CMainCtrl::m_pstMain->m_stClanMgr.CheckSelfCommend(_stClanEntryHash[iIndex]);
        }
    }

    //23点处理家族解散
    if (iNowTime >= iNextProcDelTime)
    {
        iNextProcDelTime = CTTimeUtils::GetDayBeginSec(iNowTime) + MAX_CLAN_DELAY_DELETE_TIME + 86400;
        CT_TRACE(("iNowTime: %d, iNextProcTime %d", iNowTime, iNextProcDelTime));

        CCmdDeleteClan * pstCmd = NULL;
        for (int iIndex = _stClanEntryHash.GetHeadIndex();
            iIndex >=0 ;
            iIndex = _stClanEntryHash.GetNextIndex(iIndex))
        {
            CClanEntry & stClanEntry = _stClanEntryHash[iIndex];
            if (stClanEntry._iDissMissTime > 0
                && iNowTime >= stClanEntry._iDissMissTime)
            {
                if (NULL == pstCmd)
                {
                    pstCmd = (CCmdDeleteClan *)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_DELETE_CLAN);
                    if (NULL == pstCmd)
                    {
                        CT_ERROR((""));
                        break;
                    }
                }

                pstCmd->_auiClanId.AddOneItem(stClanEntry._uiClanId);
                CT_TRACE(("add to delete clan cmd: %d", stClanEntry._uiClanId));
            }
        }

        //定期清理延迟解散的家族
        if (pstCmd)
        {
            pstCmd->pstMainCtrl = CMainCtrl::m_pstMain;
            pstCmd->Do(NULL);
        }
    }

    //周一零点结算闯关排行
    if (iNowTime >= iNextProcDiceTime)
    {
        CCmdDiceBonus * pstCmd = (CCmdDiceBonus *)CT_CMDFACTORY->CreateCmd(ID_MAIN_INNER_DICE_BONUS);
        if (NULL == pstCmd)
        {
            CT_ERROR((""));
            return ;
        }
        else
        {
            iNextProcDiceTime = CMainCtrl::m_pstMain->m_stFamilyDiceMgr.GetAccountTime (iNowTime);

            for (int iIndex = _stClanEntryHash.GetHeadIndex();
                iIndex >=0 ;
                iIndex = _stClanEntryHash.GetNextIndex(iIndex))
            {
                CClanEntry & stEntry = _stClanEntryHash[iIndex];
                if (stEntry._stDiceRank._stRank.Size() > 0)
                {
                    pstCmd->_auiClanId.AddOneItem(stEntry._uiClanId);
                }
            }

            pstCmd->pstMainCtrl = CMainCtrl::m_pstMain;
            pstCmd->Do(NULL);
        }
    }

    return ;
}

int CClanMgr::CheckSelfCommend (CClanEntry & stClanEntry)
{
    unsigned int uiLeader;
    int iRet = stClanEntry.GetClanLeader(uiLeader);
    CHECK_RETURN(iRet);

    //准备族长数据
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(
            uiLeader);
    if (iIndex < 0)
    {
        return ERROR_CLAN_LACK_SNS_INFO;
    }

    CPlayerSnsCommInfo& stPlayerSnsCommInfo =
        CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iIndex];

    int iOffLineTime = stPlayerSnsCommInfo.GetOffLineTime(
            CMainCtrl::m_pstMain->m_iNow);

    if ((iOffLineTime < MAX_CLAN_RECOMMD_TIME) ||
        (CTTimeUtils::IsSameDay(CMainCtrl::m_pstMain->m_iNow,
            stClanEntry._iLastRecommdTime)))
    {
        return 0;
    }

    iRet = SelfRecommend(stClanEntry);
    if (iRet)
    {
        CT_ERROR(("%d, %d", stClanEntry._uiClanId, iRet));
        return iRet;
    }

    CT_TRACE(("clan %d  SelfRecommend", stClanEntry._uiClanId));
    CVarObjectKey stKey;
    CClanData stClanData;
    stKey.m_chDataType = DT_CLAN_DATA;
    stKey.m_u64ID = stClanEntry._uiClanId;
    GetClanData(stClanEntry, stClanData);
    CMainCtrl::m_pstMain->UpdateData(stKey, stClanData, VOP_UPDATE);

    return 0;
}

int CClanMgr::ChooseNewLeader (CClanEntry & stClanEntry)
{
    unsigned int uiMostContriGid = 0;
    unsigned int uiViceGid = 0;

    unsigned int uiNewLeader = 0;
    stClanEntry.GetMostContriGid (CLAN_MEMBER_VICE, uiViceGid);
    if (uiViceGid > 0)
    {
        uiNewLeader = uiViceGid;
    }
    else
    {
        stClanEntry.GetMostContriGid (CLAN_MEMBER_ELITE, uiMostContriGid);
        uiNewLeader = uiMostContriGid;
    }

    if (0 == uiNewLeader)
    {
        return 0;  //如果家族没有其他成员则不进行处理
    }

    //修改族长为普通成员
    unsigned int uiOldLeader = 0;
    stClanEntry.GetClanLeader(uiOldLeader);

    int iRet = stClanEntry.SetMemberTitle (uiNewLeader, CLAN_MEMBER_LEADER);
    CHECK_RETURN(iRet);

    //如果族长被删号了, gid为0
    if (uiOldLeader > 0)
    {
        iRet = stClanEntry.SetMemberTitle (uiOldLeader, CLAN_MEMBER_NORM);
        CHECK_RETURN(iRet);
    }

    return RET_OK;
}

int CClanMgr::SelfRecommend (CClanEntry & stClanEntry)
{
    unsigned int uiOldLeader = 0;
    stClanEntry.GetClanLeader(uiOldLeader);

    int iRet = ChooseNewLeader(stClanEntry);
    CHECK_RETURN(iRet);

    unsigned int uiNewLeader = 0;
    stClanEntry.GetClanLeader(uiNewLeader);

    if (uiOldLeader == uiNewLeader)
    {
        CT_ERROR(("%d self recommend faile.", stClanEntry._uiClanId));
        return 0;
    }

    stClanEntry._iLastRecommdTime = CMainCtrl::m_pstMain->m_iNow;

    CClanLog stLog;
    stLog._uiEvent = CLAN_LOG_RECOMMEND;
    stLog._uiGid = uiOldLeader;
    stLog._uiEventGid = uiNewLeader;
    AddClanLog(stClanEntry, stLog);

    //职位变化了,通知前台,以便更新权限对应的按钮
    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiNewLeader, NOTIFY_ROLE_INFO);
    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiOldLeader, NOTIFY_ROLE_INFO);
    CMainCtrl::m_pstMain->FlushSendCache();

    CT_TRACE(("%d self recommend success.", stClanEntry._uiClanId));
    return 0;
}

int CClanMgr::GetClanLeader (unsigned int uiClanId, unsigned int & uiLeadGid)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = stClanEntry.GetClanLeader(uiLeadGid);
    CHECK_RETURN (iRet);

    return 0;
}

int CClanMgr::UpgradeClan(unsigned int uiGid, CUserClan & stUserClan)
{
    int iIndex = _stClanEntryHash.HashFind(stUserClan._uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiGid, stClanEntry, CLAN_UPGRADE_CLAN);
    CHECK_RETURN(iRet);

    if (stClanEntry._ucClanLevel >= MAX_CLAN_LEVEL)
    {
        return ERROR_CLAN_LEVEL_MAX;
    }

    CClanBuildConfig stConfigData;
    iRet = _stClanConfig.GetClanBuildConfig (stClanEntry._ucClanLevel, stConfigData);
    CHECK_RETURN(iRet);

    if (stClanEntry._iConstruction < stConfigData._iConstructionCost)
    {
        return ERROR_LACK_CONSTRUCTION;
    }

    stClanEntry._ucClanLevel += 1;
    stClanEntry._iConstruction -= stConfigData._iConstructionCost;

    CClanLog stLog;
    stLog._uiGid = uiGid;
    stLog._uiEvent = CLAN_LOG_UPGRAD;
    stLog._uiEventPara1 = stClanEntry._ucClanLevel;
    AddClanLog(stClanEntry, stLog);
    return 0;
}

int CClanMgr::Donate (CPlayerBaseInfo & stPlayerBaseInfo, CPlayerDetailInfo & stPlayerDetailInfo, CPlayerSnsDetailInfo & stPlayerSnsDetailInfo, int iDonateLZ, int iDonateSilver)
{
    //金币捐献需要的符石数
#define  DEF_CLAN_DONATE_PER_GOLD  50
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash.HashFind(stPlayerBaseInfo._uiGID);
    if (iIndex < 0)
    {
        return RET_NOT_LOGIN;
    }

    CUserStorage & stUserStorage = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash[iIndex]._stUserStorage;

    int iToAddValue = 0;

    int iDonateGold = 0;
    int iLzCnt = 0;
    if (iDonateLZ > 0)
    {
        iLzCnt = CMainCtrl::m_pstMain->m_stStorageMgr.GetItemCount(stUserStorage, ITEM_TYPE_MATERIAL, 193);
        if (iLzCnt < iDonateLZ)
        {
            iDonateGold = ((iDonateLZ - iLzCnt) * DEF_CLAN_DONATE_PER_GOLD);
            if (stPlayerBaseInfo._iGold < iDonateGold)
            {
                return ERROR_GOLD_LESS;
            }
        }
        else
        {
            iLzCnt = iDonateLZ;
        }

        iToAddValue = iDonateLZ * 100;
    }

    if (iDonateSilver > 0)
    {
        if (stPlayerBaseInfo._i64Silver < iDonateSilver)
        {
            return ERROR_SILVER_LESS;
        }

        iToAddValue = (iDonateSilver / 10000) * 1;
    }

    int iRet = AddTodayContri(stPlayerSnsDetailInfo, iToAddValue, 0, false);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d", stPlayerBaseInfo._uiGID, iToAddValue, iRet));
        return iRet;
    }

    //扣钱
    if (iDonateLZ > 0)
    {
        if (iDonateGold > 0)
        {
            CMainCtrl::m_pstMain->m_stItemMgr.SubGold(stPlayerBaseInfo, OSS_MONEY_MODULE_19, iDonateGold);
        }

        if (iLzCnt > 0)
        {
            iRet = CMainCtrl::m_pstMain->m_stStorageMgr.TakeOutItem (stUserStorage, ITEM_TYPE_MATERIAL, 193, iLzCnt);
            if (iRet)
            {
                CT_ERROR(("%d, %d, %d", iRet, stPlayerBaseInfo._uiGID, iLzCnt));
            }
            else
            {
                CMainCtrl::m_pstMain->m_stStorageMgr.GetData(stUserStorage, stPlayerDetailInfo._stUserStorage);
                CVarObjectKey stKey;
                stKey.m_chDataType = DT_PLAYER_DETAIL_INFO;
                stKey.m_u64ID = stPlayerDetailInfo._uiGID;
                CMainCtrl::m_pstMain->UpdateData(stKey, stPlayerDetailInfo, VOP_UPDATE);
            }
        }
    }

    if (iDonateSilver > 0)
    {
        iRet = CMainCtrl::m_pstMain->m_stItemMgr.SubSilver(stPlayerBaseInfo, iToAddValue * 10000, 0);
        if (iRet)
        {
            CT_ERROR(("%d", iRet));
            return iRet;
        }
    }

    CClanLog stLog;
    stLog._uiEvent = CLAN_LOG_DONATE;
    stLog._uiGid = stPlayerBaseInfo._uiGID;
    stLog._uiEventPara1 = iToAddValue;
    stLog._uiEventPara2 = iToAddValue;

    unsigned int uiClanId = stPlayerSnsDetailInfo._stCommInfo._uiClanId;
    AddClanLog(uiClanId, stLog);
    return 0;
}

int CClanMgr::AddContri (CPlayerSnsDetailInfo & stPlayerSnsDetailInfo, int iToAddValue)
{
    stPlayerSnsDetailInfo._stCommInfo._iContribution += iToAddValue;

    //触发学院中的排行改变
    CMainCtrl * pstMainCtrl = CMainCtrl::m_pstMain;
    CDailyReflushInt & stConStat = stPlayerSnsDetailInfo._stMyInfo._stPlayerStatic._stTodayContribution;
    stConStat.Add(iToAddValue,  pstMainCtrl->m_iNow);
    pstMainCtrl->m_stAcademyMgr.ConChange(stPlayerSnsDetailInfo._uiGID, stConStat.Get(pstMainCtrl->m_iNow),
        stPlayerSnsDetailInfo._stCommInfo._ucAcademy);

    return 0;
}

int CClanMgr::AddTodayContri (CPlayerSnsDetailInfo & stPlayerSnsDetailInfo, int iToAddValue, int iReputaion, bool bAddLog)
{
    if (iToAddValue <= 0)
    {
        return 0;
    }

    //修改个人贡献度
    AddContri(stPlayerSnsDetailInfo, iToAddValue);

    unsigned int uiClanId = stPlayerSnsDetailInfo._stCommInfo._uiClanId;
    if (0 == uiClanId)
    {
        return 0;
    }

    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    unsigned uiGid = stPlayerSnsDetailInfo._uiGID;
    int iRet = stClanEntry.AddTodayContri(uiGid, iToAddValue);
    CHECK_RETURN(iRet);

    if (true == bAddLog)
    {
        CClanLog stLog;
        stLog._uiGid = uiGid;
        if (0 != iReputaion)
        {
            stLog._uiEvent = CLAN_LOG_REPUT;
            stLog._uiEventPara1 = iReputaion;
            stLog._uiEventPara2 = iToAddValue;
        }
        else
        {
            stLog._uiEvent = CLAN_LOG_CONT;
            stLog._uiEventPara1 = iToAddValue;
        }

        AddClanLog(stClanEntry, stLog);
    }

    UpdateToDB(uiClanId);

    return 0;
};

void CClanMgr::NotifyClanDeleted (unsigned int uiGid)
{
    if (!CMainCtrl::m_pstMain->m_stPlayerOnline.CheckOnline(uiGid))
    {
        return ;
    }

    PBMsgHead stMsgHeader;
    stMsgHeader.set_msgret(0);
    stMsgHeader.set_msgact(0);
    stMsgHeader.set_msgtype(CSMsgDef::EMT_ANSWER);
    stMsgHeader.set_msgid(ID_MAIN_CLAN_OPER);

    MsgAnsClanOper stAnsOper;
    stAnsOper.set_oper_type(CLAN_REQ_DELETE_CLAN);
    stAnsOper.set_para1(0);  //0表示家族已解散

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHeader, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAnsOper, CMainCtrl::m_pstMain->m_stEncodeBuf);

    CMainCtrl::m_pstMain->NotifyOnlineUser(uiGid, CMainCtrl::m_pstMain->m_stEncodeBuf);

    //通知地图信息,去掉家族称号
    CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiGid, NOTIFY_ROLE_INFO | NOTIFY_MAP_INFO);

    CMainCtrl::m_pstMain->FlushSendCache();

    return ;
}

void CClanMgr::InitClanSkill (CUserClan & stUserClan)
{
    //如果玩家第一次使用家族功能,初始化家族技能
    if (0 == stUserClan._astClanSkill.Size())
    {
        for (int i = 0; i < _stClanConfig._stClanSkill.Size(); i++)
        {
            CClanSkillConfig & stSkillConfig = _stClanConfig._stClanSkill[i];
            if (true == stSkillConfig._bconfigured)
            {
                CAttrPair stSkill;
                stSkill._ucType = stSkillConfig._ucSkillId;
                stSkill._iValue = 0;
                stUserClan._astClanSkill.AddOneItem(stSkill);
            }
        }
    }

    //注意: 玩家退出家族后，学习到的家族技能依然保留.

    return ;
}

int CClanMgr::GetLearnCost (CAttrPair & stSkill, int & iContiCost)
{
    int iRet = _stClanConfig.GetLearnSkillContri (stSkill._ucType, (unsigned char)stSkill._iValue, iContiCost);
    CHECK_RETURN(iRet);

    return 0;
}

int CClanMgr::LearnSkill(unsigned char ucSkillId, CUserClan & stUserClan, int & iCurContri, unsigned int uiGid)
{
    short shClanLevel = GetClanLevel(stUserClan._uiClanId);
    if (!_stClanConfig.IsSkillOpen(shClanLevel, ucSkillId))
    {
        return ERROR_CLAN_SKILL_CLOSE;
    }

    short shRoleLevel = 0;
    CMainCtrl::m_pstMain->m_stRoleMgr.GetRoleLevel(uiGid, shRoleLevel);

    for (int i = 0; i < stUserClan._astClanSkill.Size(); i++)
    {
        if (stUserClan._astClanSkill[i]._ucType == ucSkillId)
        {
            int iContriCost = 0;
            int iRet = GetLearnCost(stUserClan._astClanSkill[i], iContriCost);
            CHECK_RETURN(iRet);

            if (iCurContri < iContriCost)
            {
                return ERROR_CLAN_LACK_CONTRI;
            }
#if 0
            if (stUserClan._astClanSkill[i]._iValue >= shClanLevel)
            {
                return ERROR_CLAN_SKILL_FULL;
            }
#else
            if (stUserClan._astClanSkill[i]._iValue >= shRoleLevel)
            {
                return ERROR_CLAN_SKILL_FULL;
            }
#endif
            stUserClan._astClanSkill[i]._iValue += 1;
            iCurContri -= iContriCost;
            return 0;
        }
    }

    return ERROR_CLAN_SKILL_CFG_NONE;
}

int CClanMgr::RefreshShop (unsigned int uiClanId)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        CT_ERROR(("%d", uiClanId));
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    //刷新商店物品
    if (CTTimeUtils::IsSameDay(CMainCtrl::m_pstMain->m_iNow, stClanEntry._stClanShop._iLastRefreshTime,
        MAX_CLAN_SHOP_REFRESH_TIME))
    {
        return 0;
    }

    int iRet = stClanEntry._stClanShop.ReNewShopItem(stClanEntry._ucClanLevel);
    if (iRet)
    {
        CT_ERROR(("%d, %d", stClanEntry._uiClanId, iRet));
        return iRet;
    }

    CClanData stClanData;
    CMainCtrl::m_pstMain->m_stClanMgr.GetClanData(stClanEntry, stClanData);

    CVarObjectKey stKey;
    stKey.m_chDataType = DT_CLAN_DATA;
    stKey.m_u64ID = stClanEntry._uiClanId;
    CMainCtrl::m_pstMain->UpdateData(stKey, stClanData, VOP_UPDATE);
    return 0;
}

int CClanMgr::GetClanInsBonus (unsigned int uiGid, unsigned char ucType, int iCount, CDropItemData & stBonusData)
{
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(uiGid);
    if (iIndex < 0)
    {
        CT_ERROR(("%d", uiGid));
        return ERROR_CLAN_GET_BONUS;
    }
    unsigned int uiClanId = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iIndex]._uiClanId;

    if (0 == uiClanId)
    {
        return ERROR_CLAN_NOT_EXIST;
    }

    iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        CT_ERROR(("%d, %d", uiGid, uiClanId));
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    CClanBuildConfig stConfigData;
    int iRet = _stClanConfig.GetClanBuildConfig(stClanEntry._ucClanLevel, stConfigData);
    if (iRet)
    {
        CT_ERROR(("%d, %d, %d", uiGid, uiClanId, iRet));
        return iRet;
    }

    if (ITEM_TYPE_SILVER == ucType)
    {
        int iExraAddtion = stConfigData._shSilverAddtion;
        if (CMainCtrl::m_pstMain->m_iNow < stClanEntry.GetAuctionBuffEndTime())
        {
            iExraAddtion += CLAN_AUCTION_SILVER_ADDITION;
        }

        stBonusData._ucItemType = ITEM_TYPE_CLAN_SILVER;
        stBonusData._iItemCount = (iCount * iExraAddtion / COMM_MOD);
        stBonusData._iData1 = iExraAddtion;   //千分比
    }

    if (ITEM_TYPE_EXP == ucType)
    {
        stBonusData._ucItemType = ITEM_TYPE_CLAN_EXP;
        stBonusData._iItemCount = (iCount * stConfigData._shExpAddtion / COMM_MOD);
        stBonusData._iData1 = stConfigData._shExpAddtion;   //千分比
    }

    stBonusData._iItemCount = CT_MAX(stBonusData._iItemCount, 1);

    return 0;
}

void CClanMgr::AddClanLog (unsigned int uiClanId, CClanLog & stLog)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        CT_ERROR(("%d", uiClanId));
        return ;
    }

    AddClanLog(_stClanEntryHash[iIndex], stLog);
    return ;
}

void CClanMgr::AddClanLog (CClanEntry & stClanEntry, CClanLog & stLog)
{
    int iRet = stClanEntry.AddLog(stLog);
    if (iRet)
    {
        return ;
    }

    //扫荡会产生大量的日志,不需要实时广播给所有人
    if (CLAN_LOG_REPUT == stLog._uiEvent || CLAN_LOG_CONT == stLog._uiEvent)
    {
        return ;
    }

    //更新成员日志
    PBMsgHead stMsgHeader;
    stMsgHeader.set_msgret(0);
    stMsgHeader.set_msgact(0);
    stMsgHeader.set_msgtype(CSMsgDef::EMT_ANSWER);
    stMsgHeader.set_msgid(ID_MAIN_USER_CLAN_INFO);

    MsgAnsUserClanInfo stAns;
    unsigned char ucLogType = 0;
    if (stLog._uiEvent <= MAX_CLAN_MEMBER_LOG_ID)
    {
        ucLogType = CLAN_LOG_MEMBER;
    }
    else
    {
        ucLogType = CLAN_LOG_EVENT;
    }

    stAns.set_oper_type(ucLogType);
    GetClanLog(stClanEntry, ucLogType, stAns);

    CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
    CMainCtrl::m_pstMain->EncodePBMsg(stMsgHeader, CMainCtrl::m_pstMain->m_stEncodeBuf);
    CMainCtrl::m_pstMain->EncodePBMsg(stAns, CMainCtrl::m_pstMain->m_stEncodeBuf);

    for (int i = 0; i < stClanEntry._astClanMember.Size(); i++)
    {
        if (CMainCtrl::m_pstMain->m_stPlayerOnline.CheckOnline(stClanEntry._astClanMember[i]._uiGid))
        {
            CMainCtrl::m_pstMain->NotifyOnlineUser(stClanEntry._astClanMember[i]._uiGid, CMainCtrl::m_pstMain->m_stEncodeBuf);
        }
    }

    if (CLAN_LOG_RECOMMEND == stLog._uiEvent)
    {
        CMainCtrl::m_pstMain->FlushSendCache();
    }
}

int CClanMgr::CheckString (char* szSrc, int iLen)
{
    //检查字符集编码方式
    CMainCtrl* pstMainCtrl = CMainCtrl::m_pstMain;
    int iRet;

    const char * sNowCharSet = pstMainCtrl->m_stCharSetConvertMgr.GetCharset(szSrc, iLen, iRet);
    if (sNowCharSet == NULL || iRet != 0)
    {
        CT_WARNING(("ret = %d, char set %s", iRet, (sNowCharSet == NULL ? "NULL" : sNowCharSet)));
        return ERROR_WRONG_NAME_CHAR_SET;
    }

    if (strncmp("UTF-8", sNowCharSet, sizeof("UTF-8")) != 0
        && strlen(sNowCharSet) != 0)
    {
        CT_WARNING(("char set %s, name = %s", (sNowCharSet == NULL ? "NULL" : sNowCharSet), szSrc));
        return ERROR_WRONG_NAME_CHAR_SET;
    }
#if 0
    //检查单字节屏蔽字
    for (int i = 0; i < iLen; ++i)
    {
        unsigned char ucChar = szSrc[i];
        if (ucChar < 128)
        {
            if ((ucChar >= '0' && ucChar <= '9')
                || (ucChar >= 'a' && ucChar <= 'z')
                || (ucChar >= 'A' && ucChar <= 'Z'))
            {
                continue;
            }
            else
            {
                return ERROR_HAS_SEN_WORD;
            }
        }
    }
#endif
    return 0;
}

int CClanMgr::GetBonusItem (CUserClan & stUserClan, CDropItem & stBonusItems)
{
    stBonusItems.Clear();
    CDropItemData stDropItemData;
    for (int i = 0; i < stUserClan._astPendingBonus.Size(); i++)
    {
        stDropItemData._ucItemType = stUserClan._astPendingBonus[i]._ucItemType;
        stDropItemData._usItemConfigID = stUserClan._astPendingBonus[i]._uiItemID;
        stDropItemData._iItemCount = stUserClan._astPendingBonus[i]._shItemCount;
        stBonusItems.Drop(stDropItemData);
    }

    stUserClan._astPendingBonus.Clear();

    return 0;
}

bool CClanMgr::CheckBonus(CUserClan & stUserClan)
{
    if (stUserClan._astPendingBonus.Size() > 0)
    {
        return true;
    }

    return false;
}

int CClanMgr::GetBonusSpace (CUserClan & stUserClan)
{
    CDropItem stItems;
    CDropItemData stDropItemData;
    for (int i = 0; i < stUserClan._astPendingBonus.Size(); i++)
    {
        stDropItemData._ucItemType = stUserClan._astPendingBonus[i]._ucItemType;
        stDropItemData._usItemConfigID = (unsigned short)stUserClan._astPendingBonus[i]._uiItemID;
        stDropItemData._iItemCount = stUserClan._astPendingBonus[i]._shItemCount;
        stItems.Drop(stDropItemData);
    }

    return stItems.GetSpaceSize();
}

int CClanMgr::AllocBonus (unsigned int uiGid, unsigned int uiClanId)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(uiGid, stClanEntry, (unsigned char)CLAN_ALLOT_BONUS);
    CHECK_RETURN(iRet);

    CClanLog stLog;
    stLog._uiGid = uiGid;
    stLog._uiEvent = CLAN_LOG_BONUS;

    AddClanLog (stClanEntry, stLog);
    return 0;
}

int CClanMgr::TakeOutItem (unsigned int uiClanId,  T_CLAN_BONUS_ITEM & stItem)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iCnt = 0;
    for (int i = 0; i < stItem.Size(); i++)
    {
        iCnt = stClanEntry._stStorage.GetItemCount(stItem[i]._ucItemType, stItem[i]._uiItemID, true);
        if (iCnt < stItem[i]._shItemCount)
        {
            CT_ERROR (("%d, %d, %d, %d", stItem[i]._ucItemType, stItem[i]._uiItemID, iCnt, stItem[i]._shItemCount));
            return ERROR_CLAN_STORAGE_LACK;
        }
    }

    for (int i = 0; i < stItem.Size(); i++)
    {
        short shLeftCount = 0;
        int iRet = stClanEntry._stStorage.TakeOutItem(stItem[i]._ucItemType, stItem[i]._uiItemID, stItem[i]._shItemCount, shLeftCount);
        if (iRet || shLeftCount > 0)
        {
            CT_ERROR (("%d, %d, %d, %d, %d", iRet, shLeftCount, stItem[i]._ucItemType, stItem[i]._uiItemID, stItem[i]._shItemCount));
            return ERROR_CLAN_STORAGE_LACK;
        }
    }

    stClanEntry._stStorage.SortStorage(true);

    return 0;
}

int CClanMgr::AddItem (unsigned int uiClanId, CDropItem & stDropItem)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    for (int i = 0; i < stDropItem.Size(); i++)
    {
        CDropItemData & stData = stDropItem._astDropItem[i];
        short shCount = stData._iItemCount;
        int iRet = stClanEntry._stStorage.AddAllItem(stData._ucItemType, stData._usItemConfigID, shCount, true);
        if (iRet || shCount > 0)
        {
            CT_ERROR(("%d, %d, %d, %d, %d, %d", iRet, stData._ucItemType, stData._usItemConfigID, stData._iItemCount, shCount, stDropItem._iModule));
        }
    }

    unsigned int uiLeader = 0;
    stClanEntry.GetClanLeader(uiLeader);
    if (uiLeader > 0)
    {
        CTips stTips;
        stTips._iType =  TIPS_CLAN_ADD_ITEM;
        CMainCtrl::m_pstMain->SendTips(uiLeader, stTips);
    }
    UpdateToDB (uiClanId);

    return 0;
}

void CClanMgr::UpdateToDB (unsigned int uiClanId)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        CT_ERROR(("clan not exist %d, %d", uiClanId));
        return ;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    CClanData stClanData;
    int iRet = GetClanData (stClanEntry, stClanData);
    if (iRet)
    {
        CT_ERROR(("%d, %d", uiClanId, iRet));
        return ;
    }

    CVarObjectKey stKey;
    stKey.m_chDataType = DT_CLAN_DATA;
    stKey.m_u64ID = stClanEntry._uiClanId;
    stClanData._uiClanID = stClanEntry._uiClanId;
    CMainCtrl::m_pstMain->UpdateData(stKey, stClanData, VOP_UPDATE);
    return ;
}

int CClanMgr::GetClanIndex (unsigned int uiGid)
{
    if (0 == uiGid)
    {
        return -1;
    }

    CMainCtrl * pMainCtrl = CMainCtrl::m_pstMain;
    int iIndex = pMainCtrl->m_stPlayerSnsCommInfoHash.HashFind(uiGid);
    if (iIndex < 0)
    {
        CT_ERROR(("%d", uiGid));
        return -1;
    }

    unsigned int uiClanId = pMainCtrl->m_stPlayerSnsCommInfoHash[iIndex]._uiClanId;
    return _stClanEntryHash.HashFind(uiClanId);
}

int CClanMgr::GetClanTitle (unsigned int uiGid, int & iTitle)
{
    int iIndex = GetClanIndex (uiGid);
    if (iIndex < 0)
    {
        iTitle = CLAN_MEMBER_NONE;
        return 0;
    }

    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];
    CClanMember stMember;
    int iRet = stClanEntry.GetClanMember(uiGid, stMember);
    if (iRet)
    {
        iTitle = CLAN_MEMBER_NONE;
        CT_ERROR(("%d,%d", uiGid, stClanEntry._uiClanId));
        return ERROR_CLAN_MEMBER_NOT_EXIST;
    }

    iTitle = stMember._ucClanTitle;
    return 0;
}

int CClanMgr::GetClanLevel (unsigned int uiClanId)
{
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return 0;
    }

    return _stClanEntryHash[iIndex]._ucClanLevel;
}

bool CClanMgr::IsInSameClan (unsigned int uiGid1, unsigned int uiGid2)
{
    int iIndex1 = GetClanIndex (uiGid1);
    int iIndex2 = GetClanIndex (uiGid2);

    if (iIndex1 > 0 && (iIndex1 == iIndex2))
    {
        return true;
    }

    return false;
}

int CClanMgr::SetAuctionBuff (unsigned int uiGid, int iEndTime)
{
    int iIndex = GetClanIndex (uiGid);
    if (iIndex < 0)
    {
        return 0;
    }

    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];
    int iNow = CMainCtrl::m_pstMain->m_iNow;
    int iOldTime = stClanEntry.GetAuctionBuffEndTime();
    if (iNow < iOldTime)
    {
        CT_ERROR(("%d, %d, %d", stClanEntry._uiClanId, iNow, iOldTime));
        return ERROR_SET_AUCTION_TIME;
    }

    stClanEntry.SetAuctionBuffTime(iEndTime);
    UpdateToDB(stClanEntry._uiClanId);
    return 0;
}

CClanEntry & CClanMgr::GetClanEntry (unsigned int uiClanIndex)
{
    return _stClanEntryHash[uiClanIndex];
}

bool CClanMgr::IsValidIndex (unsigned int uiClanIndex)
{
    if (uiClanIndex < MAX_CLAN_COUNT_PER_ZONE)
    {
        return true;
    }

    return false;
}

int CClanMgr::GetClanRank (unsigned int uiGid)
{
    int iIndex = GetClanIndex(uiGid);
    if (iIndex < 0)
    {
        return 0;
    }

    CClanEntry & stClanEntry = GetClanEntry (iIndex);
    return stClanEntry._usClanRank;
}

int CClanMgr::GetClanInfo (int iPageBeg, int iPageEnd, int iPageSize, CUserClan & stUserClan, ::google::protobuf::RepeatedPtrField< ::MsgClanBaseInfo > & stAns, int & iTotalPages)
{
    int iPerPageCount = iPageSize;
    if (iPerPageCount <= 0)
    {
        CT_ERROR(("%d", iPerPageCount));
        return ERROR_CLAN_PAGE_PARA;
    }

    int iTotalClanNum = _astClanRank.Size();
    iTotalPages = (iTotalClanNum / iPerPageCount + (iTotalClanNum % iPerPageCount ? 1 : 0));
    if (iPageBeg < 1)
    {
        CT_ERROR(("%d, %d,%d", iPerPageCount, iPageBeg, iTotalClanNum));
        return ERROR_CLAN_PAGE_PARA;
    }

    int iPageNo = 0;
    for (int i = 0; i < iTotalClanNum; ++i)
    {
        CClanEntry & stClanEntry = _stClanEntryHash[_astClanRank[i]];

        iPageNo = i / iPerPageCount;
        if ((iPageNo + 1) < iPageBeg || (iPageNo + 1)> iPageEnd)
        {
            continue;
        }

        MsgClanBaseInfo * pstBaseInfo = stAns.Add();
        int iRet = GetClanBaseInfo(stClanEntry, stUserClan, *pstBaseInfo);
        if (iRet)
        {
            CT_ERROR (("%d, %d", stClanEntry._uiClanId, iRet));
            continue;
        }
    }

    return 0;
}

int CClanMgr::AddToClan (unsigned int uiGid, CUserClan & stUserClan, CClanEntry & stClanEntry, EN_CLAN_MEMBER_TYPE enTitle)
{
    if (stUserClan._uiClanId > 0)
    {
        return ERROR_ALREADY_IN_CLAN;
    }

    int iIndex = CMainCtrl::m_pstMain->m_stPlayerDetailInfoHash.HashFind(uiGid);
    if (iIndex < 0)
    {
        CT_ERROR(("%d, %d", iIndex, uiGid));
        return  RET_NO_USER;
    }
    CPlayerDetailInfo & stDetail = CMainCtrl::m_pstMain->m_stPlayerDetailInfoHash[iIndex];

    CClanMember stMember;
    stMember._uiGid = uiGid;
    stMember._ucClanTitle = enTitle;
    stMember._iTodayContri = 0;
    stMember._iLastCalcTime = CMainCtrl::m_pstMain->m_iNow;

    int iRet = stClanEntry.AddNewMember(stMember);
    CHECK_RETURN(iRet);

    stUserClan._uiClanId = stClanEntry._uiClanId;
    stUserClan._iJoinTime = CMainCtrl::m_pstMain->m_iNow;
    stUserClan._uiJoinedCount += 1;

    InitClanSkill(stUserClan);

    //清空摘果次数
    stUserClan._stTreeData.ClearFruitCount();

    SetSnsClanId (uiGid, stClanEntry._uiClanId);

    if (CMainCtrl::m_pstMain->m_stPlayerOnline.CheckOnline(uiGid))
    {
        CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiGid, NOTIFY_MAP_INFO);
        CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(uiGid, NOTIFY_ROLE_INFO);
    }

    //加入到闯关排行榜中
    CMainCtrl::m_pstMain->m_stFamilyDiceMgr.InitDiceBestRecord(stDetail, CMainCtrl::m_pstMain->m_iNow);
    stClanEntry.UpdateDiceRank(uiGid, stDetail._stDiceData._iWeekBestGrid);

    return 0;
}

int CClanMgr::DelFromClan (unsigned int uiGid, CUserClan & stUserClan, CClanEntry & stClanEntry)
{
    if (stUserClan._uiClanId != stClanEntry._uiClanId)
    {
        return ERROR_CLAN_MEMBER_NOT_EXIST;
    }

    stUserClan.ClearWhenNoClan();

    SetSnsClanId (uiGid, 0);

    stClanEntry.RemoveMember(uiGid);

    if (CMainCtrl::m_pstMain->m_stPlayerOnline.CheckOnline(uiGid))
    {
        int iIndex = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash.HashFind(uiGid);
        if (iIndex >= 0)
        {
            CPlayerOnlineData & stOnlineData = CMainCtrl::m_pstMain->m_stPlayerOnlineDataHash[iIndex];

            //在家族大厅地图内, 退出家族（或被踢出家族）后要强制退出家族大厅地图
            if (MAP_ID_CLAN_HALL == stOnlineData._uiNowMapID)
            {
                int iIndex = CMainCtrl::m_pstMain->m_stPlayerBaseInfoHash.HashFind(uiGid);
                if (iIndex >= 0)
                {
                    CPlayerBaseInfo & stBaseInfo = CMainCtrl::m_pstMain->m_stPlayerBaseInfoHash[iIndex];
                    MsgReqMoveTo stMoveTo;
                    stMoveTo.set_gid(uiGid);
                    stMoveTo.set_map_id(stBaseInfo._uiMapID);
                    stMoveTo.set_pos_x(stBaseInfo._usXPos);
                    stMoveTo.set_pos_y(stBaseInfo._usYPos);
                    CMainCtrl::m_pstMain->SendInnerCmd (NULL, uiGid, ID_MAIN_MAP_MOVE, stMoveTo, true);
                }
                else
                {
                    CT_ERROR(("%d", uiGid));
                }
            }
        }
        else
        {
            CT_ERROR(("%d", uiGid));
        }
    }

    //玩家离开家族后就从排行中清除该玩家
    stClanEntry._stTree.ShiftDelInRank(uiGid);

    //从闯关排行榜中清除该玩家
    stClanEntry._stDiceRank.ShiftDelInRank(uiGid);

    return 0;
}

void CClanMgr::SetSnsClanId (unsigned int uiGid, unsigned int uiClanId)
{
    int iIndex = CMainCtrl::m_pstMain->m_stPlayerSnsDetailInfoHash.HashFind(uiGid);
    if (iIndex >= 0)
    {
        CPlayerSnsDetailInfo& stPlayerSnsDetailInfo = CMainCtrl::m_pstMain->m_stPlayerSnsDetailInfoHash[iIndex];
        stPlayerSnsDetailInfo._stCommInfo._uiClanId = uiClanId;

        CVarObjectKey stKey;
        stKey.m_u64ID = uiGid;
        stKey.m_chDataType = DT_PLAYER_SNS_DETAIL_INFO;
        CMainCtrl::m_pstMain->UpdateData(stKey, stPlayerSnsDetailInfo, VOP_UPDATE);
    }
    else
    {
        CT_ERROR(("%d, %d", uiGid, uiClanId));
    }
}

const char * CClanMgr::GetUserClanName (unsigned int uiGid)
{
    int iIndex = GetClanIndex(uiGid);
    if (iIndex < 0)
    {
        return "";
    }

    return _stClanEntryHash[iIndex]._szName();
}

void CClanMgr::DelAllJoinRequest (unsigned int uiGid, CUserClan & stUserClan)
{
    while (stUserClan._astJoinReq.Size() > 0)
    {
        int iReqIndex = stUserClan._astJoinReq.Size() - 1;
        int iRet = CancelJoinRequest (uiGid, stUserClan, stUserClan._astJoinReq[iReqIndex]);
        if (iRet)
        {
            CT_ERROR(("Gid: %u, clanid: %u", uiGid, stUserClan._astJoinReq[iReqIndex]));
        }

        stUserClan._astJoinReq.DelOneItem(iReqIndex);
    }
}

int CClanMgr::GetClanLog (CClanEntry & stClanEntry, unsigned char ucLogType, MsgAnsUserClanInfo & stAns)
{
    for (int i = 0; i < stClanEntry._astClanLog.Size(); i++)
    {
        CClanLog & stLog = stClanEntry._astClanLog[i];

        MsgClanLog * pstClanLog = 0;
        if (CLAN_LOG_MEMBER == ucLogType)
        {
            if (stLog._uiEvent > MAX_CLAN_MEMBER_LOG_ID)
            {
                continue;
            }

            pstClanLog = stAns.add_member_logs();
        }
        else
        {
            if (stLog._uiEvent <= MAX_CLAN_MEMBER_LOG_ID)
            {
                continue;
            }

            pstClanLog = stAns.add_clan_logs();
        }

        if (0 == pstClanLog)
        {
            CT_ERROR((""));
            continue;
        }

        pstClanLog->set_event(stLog._uiEvent);
        pstClanLog->set_time(stLog._iTime);
        pstClanLog->set_gid1(stLog._uiGid);
        if (0 != stLog._uiGid)
        {
            int iGidIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(stLog._uiGid);
            if (iGidIndex < 0)
            {
                CT_ERROR(("%d", stLog._uiGid));
                continue;
            }

            pstClanLog->set_name1(CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iGidIndex]._sRoleName());
        }

        pstClanLog->set_gid2(stLog._uiEventGid);
        if (0 != stLog._uiEventGid)
        {
            int iGidIndex = CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash.HashFind(stLog._uiEventGid);
            if (iGidIndex < 0)
            {
                CT_ERROR(("%d", stLog._uiEventGid));
                continue;
            }

            pstClanLog->set_name2(CMainCtrl::m_pstMain->m_stPlayerSnsCommInfoHash[iGidIndex]._sRoleName());
        }

        pstClanLog->set_para1(stLog._uiEventPara1);
        pstClanLog->set_para2(stLog._uiEventPara2);
    }

    return 0;
}

bool CClanMgr::CanDoDailOpt (CPlayerBaseInfo & stPlayerBaseInfo, CPlayerDetailInfo & stPlayerDetailInfo, int iNowTime)
{
    if (0 == stPlayerDetailInfo._stUserClan._uiClanId)
    {
        return false;
    }

    int iMaxDailCnt = 0;
    int iCount = GetLeftDailCount (stPlayerBaseInfo._chVipLevel, stPlayerDetailInfo, iNowTime, iMaxDailCnt);

    return (iCount > 0);
}

int CClanMgr::GetLeftDailCount (char chVipLevel, CPlayerDetailInfo & stPlayerDetailInfo, int iNowTime, int & iMaxDailCnt)
{
    int iRet = CMainCtrl::m_pstMain->m_stVipMgr.GetVipDestNum(chVipLevel, VIP_MODULE_ID_14, iMaxDailCnt);
    if (iRet)
    {
        CT_ERROR(("%d, %d", chVipLevel, iRet));
        return 0;
    }

    int iUsedCount = stPlayerDetailInfo._stUserClan._stDailCnt.Get(iNowTime, CLAN_DAIL_RESET_TIME);
    return CT_MAX((iMaxDailCnt - iUsedCount), 0);
}

int CClanMgr::Recruit (CPlayerBaseInfo & stPlayerBaseInfo, CUserClan & stUserClan)
{
    unsigned int uiClanId = stUserClan._uiClanId;

    //检查家族是否存在
    int iIndex = _stClanEntryHash.HashFind(uiClanId);
    if (iIndex < 0)
    {
        return ERROR_CLAN_NOT_EXIST;
    }
    CClanEntry & stClanEntry = _stClanEntryHash[iIndex];

    int iRet = CheckPermission(stPlayerBaseInfo._uiGID, stClanEntry, (unsigned char)CLAN_SEND_RECRUIT);
    CHECK_RETURN(iRet);

    int iNowTime = CMainCtrl::m_pstMain->m_iNow;
    int iUsedCnt = stClanEntry.GetTodayRecuitCnt(iNowTime);
    int iNeedGold = (iUsedCnt < MAX_RECURIT_FREE_CNT ? 0 : DEF_CLAN_RECURIT_GOLD);
    if (stPlayerBaseInfo._iGold < iNeedGold)
    {
        return ERROR_GOLD_LESS;
    }

    if (iNeedGold > 0)
    {
        CMainCtrl::m_pstMain->m_stItemMgr.SubGold(stPlayerBaseInfo, OSS_MONEY_MODULE_53, iNeedGold);
        CMainCtrl::m_pstMain->m_stNotifyMgr.Notify(stPlayerBaseInfo._uiGID, NOTIFY_USER_MONEY);
    }

    stClanEntry.AddTodayRecuitCnt(iNowTime);

    //发送聊天频道消息
    MsgTips stTips;
    stTips.set_type(1078);
    stTips.add_tip_argument(CTStringUtils::IntToString(stClanEntry._ucAcademyType));
    stTips.add_tip_argument(stClanEntry._szName());
    stTips.add_tip_argument(CTStringUtils::IntToString(uiClanId));
    CMainCtrl::m_pstMain->SendOnlineTips(stTips);

    //更新族长\副族长的招募次数
    T_CLAN_MANAGER stManager;
    stClanEntry.GetManager(stManager);
    if (stManager.Size() > 1)
    {
        PBMsgHead stMsgHeader;
        stMsgHeader.set_msgret(0);
        stMsgHeader.set_msgact(0);
        stMsgHeader.set_msgtype(CSMsgDef::EMT_ANSWER);
        stMsgHeader.set_msgid(ID_MAIN_CLAN_OPER);

        MsgAnsClanOper stAnsOper;
        stAnsOper.set_oper_type(CLAN_SEND_RECRUIT);
        GetClanBaseInfo(stClanEntry, stUserClan, *stAnsOper.mutable_clan_base_info());

        CMainCtrl::m_pstMain->m_stEncodeBuf.Clear();
        CMainCtrl::m_pstMain->EncodePBMsg(stMsgHeader, CMainCtrl::m_pstMain->m_stEncodeBuf);
        CMainCtrl::m_pstMain->EncodePBMsg(stAnsOper, CMainCtrl::m_pstMain->m_stEncodeBuf);

        for (int i = 0; i < stManager.Size(); i++)
        {
            unsigned uiGid = stManager[i];
            if (uiGid != stPlayerBaseInfo._uiGID)
            {
                CMainCtrl::m_pstMain->NotifyOnlineUser(uiGid, CMainCtrl::m_pstMain->m_stEncodeBuf);
            }
        }
    }

    return RET_OK;
}
